package genetic;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;

import functionTypes.ConstantSampling;
import functionTypes.Interpolation;
import functionTypes.SamplingFunction;
import functionTypes.OldFunction;
import tasks.Boiler;
import tasks.BoilerControlSystem;
import Abstracts.AbstractControlSystem;
import Abstracts.AbstractFunction;
import Abstracts.AbstractGeneticFunction;
import Abstracts.AbstractSinglePropertyFunction;

public class HillClimbing {

	public AbstractControlSystem[] individuals;
	public double[] scores;
	public String task;
	public String type;
	public int typeIndex;
	public int individualsCount = 60;
	public int maxEvolutionCount = 100;
	public double mutationProbability = 0.3;

	public double bestIndividualScore = Double.POSITIVE_INFINITY;
	public AbstractControlSystem bestIndividual;
	public double[] bestIndividualsScores;

	public Boiler boiler;

	public HillClimbing(String task, String type, int individualsCount,
			int maxEvolutionCount, double mutationProbability) throws Exception {
		if (individualsCount > 2) {
			this.individualsCount = individualsCount;
		}

		scores = new double[individualsCount];

		if (maxEvolutionCount > 2) {
			this.maxEvolutionCount = maxEvolutionCount;
		}

		if ((mutationProbability > 0.3) && (mutationProbability < 1)) {
			this.mutationProbability = mutationProbability;
		}

		if (!(type.equals("old") || type.equals("sampling")
				|| type.equals("constantSampling") || type
				.equals("interpolation"))) {
			throw new Exception(
					"Types may be: old, sampling, constantSampling, interpolation");
		}

		if (task.equals("boiler")) {
			this.task = "boiler";
			this.type = type;
			boiler = new Boiler();
			createBoilerField();
			boilerHillClimbing();
		}
	}

	public BoilerControlSystem getNext(BufferedReader br) throws Exception {
		ConstantSampling[] f = new ConstantSampling[5];
		for (int j = 0; j != 5; ++j) {
			f[j] = new ConstantSampling();
		}

		StringTokenizer st = new StringTokenizer(br.readLine());

		int i = 0;
		for (; i != 200; ++i) {
			if (st.nextToken().charAt(0) == '/') {
				if (i > 100) {
					for (; i != 200; ++i) {
						for (int k = 0; k != 5; ++k) {
							f[k].subfunctions[i] = f[k].subfunctions[i - 1]
									.clone();
						}
					}
				} else {
					st = new StringTokenizer(br.readLine());
				}
			} else {
				for (int k = 0; k != 5; ++k) {
					f[k].subfunctions[i].getCoeff()[0] = Double.parseDouble(st
							.nextToken());
				}
				st = new StringTokenizer(br.readLine());
			}
		}

		BoilerControlSystem cs = new BoilerControlSystem(f);
		return cs;
	}

	public void createBoilerField() {
		individuals = new BoilerControlSystem[individualsCount];
		bestIndividualsScores = new double[maxEvolutionCount + 1];
		typeIndex = 0;

		if (type.equals("old")) {
			typeIndex = 0;
		} else if (type.equals("sampling")) {
			typeIndex = 1;
		} else if (type.equals("constantSampling")) {
			typeIndex = 2;
		} else if (type.equals("interpolation")) {
			typeIndex = 3;
		}

		for (int i = 0; i != individualsCount; ++i) {
			individuals[i] = new BoilerControlSystem();
			if (typeIndex == 0) {
				OldFunction[] functions = new OldFunction[individuals[i]
						.getDimensions()];
				for (int k = 0; k != functions.length; ++k) {
					functions[k] = new OldFunction();
					functions[k].generateFunction();
				}
				individuals[i].setControlSystem(functions);
			} else if (typeIndex == 1) {
				SamplingFunction[] functions = new SamplingFunction[individuals[i]
						.getDimensions()];
				for (int k = 0; k != functions.length; ++k) {
					functions[k] = new SamplingFunction();
					functions[k].generateFunction();
				}
				individuals[i].setControlSystem(functions);
			} else if (typeIndex == 2) {
				ConstantSampling[] functions = new ConstantSampling[individuals[i]
						.getDimensions()];
				for (int k = 0; k != functions.length; ++k) {
					functions[k] = new ConstantSampling();
					functions[k].generateFunction();
				}
				individuals[i].setControlSystem(functions);
			} else if (typeIndex == 3) {
				Interpolation[] functions = new Interpolation[individuals[i]
						.getDimensions()];
				for (int k = 0; k != functions.length; ++k) {
					functions[k] = new Interpolation();
					functions[k].generateFunction();
				}
				individuals[i].setControlSystem(functions);
			}
			scores[i] = boiler.getEffectivnes(individuals[i]);
		}
	}

	public void boilerHillClimbing() throws Exception {
		for (int i = 0; i < maxEvolutionCount; ++i) {
			AbstractControlSystem[] newSystems = new BoilerControlSystem[individualsCount];
			double[] newResults = new double[individualsCount];

			for (int k = 0; k != individualsCount; ++k) {
				// individuals[k].clone(newSystems[k]);//copy not link
				newSystems[k] = ((BoilerControlSystem) individuals[k]).clone();
				AbstractFunction[] functions = newSystems[k].getFunctions();
				for (int q = 0; q != functions.length; ++q) {
					for (int qwe = 0; qwe < functions[q].getSubFunctions().length; ++qwe) {
						((AbstractGeneticFunction) functions[q])
								.mutate(mutationProbability);
					}
				}
				newResults[k] = boiler.getEffectivnes(newSystems[k]);
			}

			AbstractControlSystem[] best = new BoilerControlSystem[individualsCount];
			double[] bestResults = new double[individualsCount];

			for (int j = 0; j != individualsCount; ++j) {
				int bestIndex1 = 0;
				int bestIndex2 = 0;

				for (int k = 0; k != newResults.length; ++k) {
					if (newResults[k] != 0) {
						if (newResults[k] < newResults[bestIndex2]) {
							bestIndex2 = k;
						}
					}
				}

				for (int k = 0; k != individuals.length; ++k) {
					if (scores[k] != 0) {
						if (scores[k] < scores[bestIndex1]) {
							bestIndex1 = k;
						}
					}
				}

				if (scores[bestIndex1] < newResults[bestIndex2]) {
					if (scores[bestIndex1] != 0) {
						best[j] = individuals[bestIndex1];
						bestResults[j] = scores[bestIndex1];
						scores[bestIndex1] = Double.POSITIVE_INFINITY;
					}
				} else {
					if (scores[bestIndex2] != 0) {
						best[j] = newSystems[bestIndex2];
						bestResults[j] = newResults[bestIndex2];
						newResults[bestIndex2] = Double.POSITIVE_INFINITY;
					}
				}
			}
			individuals = best;
			scores = bestResults;

			for (int qwe = 0; qwe != scores.length; ++qwe) {
				if (scores[qwe] < bestIndividualScore) {
					bestIndividualScore = scores[qwe];
					bestIndividualsScores[i] = scores[qwe];
					bestIndividual = individuals[qwe];
				}
			}
			bestIndividualsScores[i] = bestIndividualScore;
			System.out.println("Best score:" + bestIndividualScore);
			System.out.println("\nEvolution step:" + i);
		}
		for (int qwe = 0; qwe != scores.length; ++qwe) {
			if (scores[qwe] < bestIndividualScore) {
				bestIndividualScore = scores[qwe];
				bestIndividualsScores[maxEvolutionCount] = scores[qwe];
				bestIndividual = individuals[qwe];
			}
		}
		bestIndividualsScores[maxEvolutionCount] = bestIndividualScore;

		System.out.println("Best score:" + bestIndividualScore);
	}

	public void setTypeDependOnAlgo(AbstractSinglePropertyFunction f) {
		if (typeIndex == 0) {
			f.rebuild();
		} else if (typeIndex == 1) {
			f.setDefaultType();
		} else if (typeIndex == 2) {
			f.setConstantType();
		}
	}

}
